package syM

import (
	"database/sql"
	"fmt"
	"db2s/Er"
	"db2s/Meta"
	metaD "db2s/MetaInit"
	"db2s/arg"
	"db2s/global"
	"db2s/go-log/log"
	"db2s/outPut"
)

func getSchemaObjectMetaEr(drive string) (res Er.SchemaObjectEr, err error) {
	if res, err = Er.SchemaObject(Er.SchemaObjectMeta{DBType: drive}); err != nil {
		return
	}
	return
}
func getInputTableAttributesInput(s global.Object) arg.InputTableAttributes {
	var static global.StartPartConfigInputP
	switch GlobalPConfigs.rules.CheckMode {
	case "object":
		static = global.StartPartConfigInputP{
			SqlMode: GlobalPConfigs.rules.Object.SqlMode,
		}
	case "struct":
		static = global.StartPartConfigInputP{
			SqlMode: GlobalPConfigs.rules.Struct.SqlMode,
		}
	}
	return arg.InputTableAttributes{
		Schema: s.Schema,
		Table:  s.Table,
		MSchema: func() string {
			if len(s.MSchema) == 0 {
				return s.Schema
			}
			return s.MSchema
		}(),
		MTable: func() string {
			if len(s.MTable) == 0 {
				return s.Table
			}
			return s.MTable
		}(),
		TableAttributes: arg.TableSubOptions{
			Role: s.Role,
		},
		TableJoin: "right",
		Static:    static,
	}
}

func initToActiveObject(s global.Object) (exist bool, err error) {
	var (
		event = "[initToActiveObject]"
	)
	l := metaD.GetTableFilterO()
	l.TableMetaData = GlobalISObjects.TableMetaData
	//l.SDB = GlobalPConfigs.SDB
	//l.DDB = GlobalPConfigs.DDB
	l.SourceDrive = GlobalPConfigs.dSns.SrcDBName
	l.DestDrive = GlobalPConfigs.dSns.DestDBName
	l.SqlMode = GlobalPConfigs.rules.Object.SqlMode
	if exist, err = l.InitToActiveObject(getInputTableAttributesInput(s)); err != nil {
		log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v init active object fail. error is %v", s.ObjectLogSeq, event, s.MSchema, s.MTable, err))
		return
	}
	return
}
func (sp SchedulePlan2) getSchemaObjectInputMap(create bool) map[string]global.SchemaObjectInput {
	input := global.StartPartConfigInputP{
		SqlMode: GlobalPConfigs.rules.Object.SqlMode,
		//SqlExecStopTime: GlobalPConfigs.rules.Object.,
	}
	if !create {
		return map[string]global.SchemaObjectInput{
			GlobalPConfigs.dSns.SrcDBName: {
				//DB:     GlobalPConfigs.SDB,
				Schema: sp.Object.Schema,
				Table:  sp.Object.Table,
				Input:  input,
			},
		}
	}
	return map[string]global.SchemaObjectInput{
		GlobalPConfigs.dSns.SrcDBName: {
			//DB:     GlobalPConfigs.SDB,
			Schema: sp.Object.Schema,
			Table:  sp.Object.Table,
			Input:  input,
		},
		GlobalPConfigs.dSns.DestDBName: {
			//DB:     GlobalPConfigs.DDB,
			Schema: sp.Object.MSchema,
			Table:  sp.Object.MTable,
			Input:  input,
		},
	}
}
func (sp SchedulePlan2) getJobDefinitionBody(create bool) (res global.SrcDstJobObjectResult, err error) {
	var (
		t     Er.SchemaObjectEr
		r     global.SchemaObjectReturnResult
		event = "[getJobDefinitionBody]"
	)
	for drive, input := range sp.getSchemaObjectInputMap(create) {
		if t, err = getSchemaObjectMetaEr(drive); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v init %v Job interface fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		if r, err = t.Job(input); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v get %v Job fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		switch drive {
		case GlobalPConfigs.dSns.SrcDBName:
			res.Src = r.JobResult
		case GlobalPConfigs.dSns.DestDBName:
			res.Dst = r.JobResult
		}
	}
	return
}
func (sp SchedulePlan2) getViewDefinitionBody(create bool) (res global.SrcDstViewObjectResult, err error) {
	var (
		t     Er.SchemaObjectEr
		r     global.SchemaObjectReturnResult
		event = "[getViewDefinitionBody]"
	)
	for drive, input := range sp.getSchemaObjectInputMap(create) {
		if t, err = getSchemaObjectMetaEr(drive); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v init %v View interface fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		if r, err = t.View(input); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v get %v View fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		switch drive {
		case GlobalPConfigs.dSns.SrcDBName:
			res.Src = r.ViewResult
		case GlobalPConfigs.dSns.DestDBName:
			res.Dst = r.ViewResult
		}
	}
	return
}
func (sp SchedulePlan2) getSeqDefinitionBody(create bool) (res global.SrcDstSeqObjectResult, err error) {
	var (
		t     Er.SchemaObjectEr
		r     global.SchemaObjectReturnResult
		event = "[getSeqDefinitionBody]"
	)
	for drive, input := range sp.getSchemaObjectInputMap(create) {
		if t, err = getSchemaObjectMetaEr(drive); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v init %v Seq interface fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}

		if r, err = t.Seq(input); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v get %v Seq fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		switch drive {
		case GlobalPConfigs.dSns.SrcDBName:
			res.Src = r.SeqResult
		case GlobalPConfigs.dSns.DestDBName:
			res.Dst = r.SeqResult
		}
	}
	return
}
func (sp SchedulePlan2) getFuncDefinitionBody(create bool) (res global.SrcDstFuncObjectResult, err error) {
	var (
		t     Er.SchemaObjectEr
		r     global.SchemaObjectReturnResult
		event = "[getFuncDefinitionBody]"
	)
	for drive, input := range sp.getSchemaObjectInputMap(create) {
		if t, err = getSchemaObjectMetaEr(drive); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v init %v Func interface fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		if r, err = t.Func(input); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v get %v Func fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		switch drive {
		case GlobalPConfigs.dSns.SrcDBName:
			res.Src = r.FuncResult
		case GlobalPConfigs.dSns.DestDBName:
			res.Dst = r.FuncResult
		}
	}
	return
}
func (sp SchedulePlan2) getProcedureDefinitionBody(create bool) (res global.SrcDstProcedureObjectResult, err error) {
	var (
		t     Er.SchemaObjectEr
		r     global.SchemaObjectReturnResult
		event = "[getProcedureDefinitionBody]"
	)
	for drive, input := range sp.getSchemaObjectInputMap(create) {
		if t, err = getSchemaObjectMetaEr(drive); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v init %v Procedure interface fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		if r, err = t.Procedure(input); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v get %v Procedure fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		switch drive {
		case GlobalPConfigs.dSns.SrcDBName:
			res.Src = r.ProcedureResult
		case GlobalPConfigs.dSns.DestDBName:
			res.Dst = r.ProcedureResult
		}
	}
	return
}
func (sp SchedulePlan2) getTriggerDefinitionBody(create bool) (res global.SrcDstTriggerObjectResult, err error) {
	var (
		t     Er.SchemaObjectEr
		r     global.SchemaObjectReturnResult
		event = "[getTriggerDefinitionBody]"
	)
	for drive, input := range sp.getSchemaObjectInputMap(create) {
		if t, err = getSchemaObjectMetaEr(drive); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v init %v Trigger interface fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		if r, err = t.Trigger(input); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v get %v Trigger fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		switch drive {
		case GlobalPConfigs.dSns.SrcDBName:
			res.Src = r.TriggerResult
		case GlobalPConfigs.dSns.DestDBName:
			res.Dst = r.TriggerResult
		}
	}
	return
}
func (sp SchedulePlan2) getForeignDefinitionBody(create bool) (res global.SrcDstForeignObjectResult, err error) {
	var (
		t     Er.SchemaObjectEr
		r     global.SchemaObjectReturnResult
		event = "[getForeignDefinitionBody]"
	)
	for drive, input := range sp.getSchemaObjectInputMap(create) {
		if t, err = getSchemaObjectMetaEr(drive); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v init %v Foreign interface fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		if r, err = t.Foreign(input); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v get %v Foreign fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		switch drive {
		case GlobalPConfigs.dSns.SrcDBName:
			res.Src = r.ForeignResult
		case GlobalPConfigs.dSns.DestDBName:
			res.Dst = r.ForeignResult
		}
	}
	return
}
func (sp SchedulePlan2) getPackageDefinitionBody(create bool) (res global.SrcDstPackageObjectResult, err error) {
	var (
		t     Er.SchemaObjectEr
		r     global.SchemaObjectReturnResult
		event = "[getPackageDefinitionBody]"
	)
	for drive, input := range sp.getSchemaObjectInputMap(create) {
		if t, err = getSchemaObjectMetaEr(drive); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v init %v Package interface fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		if r, err = t.Package(input); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v get %v Package fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		switch drive {
		case GlobalPConfigs.dSns.SrcDBName:
			res.Src = r.PackageResult
		case GlobalPConfigs.dSns.DestDBName:
			res.Dst = r.PackageResult
		}
	}
	return
}
func (sp SchedulePlan2) getTypeDefinitionBody(create bool) (res global.SrcDstTypeObjectResult, err error) {
	var (
		t     Er.SchemaObjectEr
		r     global.SchemaObjectReturnResult
		event = "[getTypeDefinitionBody]"
	)
	for drive, input := range sp.getSchemaObjectInputMap(create) {
		if t, err = getSchemaObjectMetaEr(drive); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v  %v.%v init %v Type interface fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		if r, err = t.Type(input); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v.%v get %v Type fail. error is %v", sp.Object.ObjectLogSeq, event, sp.Object.MSchema, sp.Object.MTable, drive, err))
			return
		}
		switch drive {
		case GlobalPConfigs.dSns.SrcDBName:
			res.Src = r.TypeResult
		case GlobalPConfigs.dSns.DestDBName:
			res.Dst = r.TypeResult
		}
	}
	return
}
func getCreateSchemaObjectInput(role string, s global.GetObjectMeta) global.SchemaObjectReturnResult {
	switch role {
	case "job":
		return global.SchemaObjectReturnResult{
			JobResult: s.JobResult.Src,
		}
	case "view":
		return global.SchemaObjectReturnResult{
			ViewResult: s.ViewResult.Src,
		}
	case "seq":
		return global.SchemaObjectReturnResult{
			SeqResult: s.SeqResult.Src,
		}
	case "trigger":
		return global.SchemaObjectReturnResult{
			TriggerResult: s.TriggerResult.Src,
		}
	case "foreign":
		return global.SchemaObjectReturnResult{
			ForeignResult: s.ForeignResult.Src,
		}
	case "func":
		return global.SchemaObjectReturnResult{
			FuncResult: s.FuncResult.Src,
		}
	case "procedure":
		return global.SchemaObjectReturnResult{
			ProcedureResult: s.ProcedureResult.Src,
		}
	case "package":
		return global.SchemaObjectReturnResult{
			PackageResult: s.PackageResult.Src,
		}
	case "type":
		return global.SchemaObjectReturnResult{
			TypeResult: s.TypeResult.Src,
		}
	}
	return global.SchemaObjectReturnResult{}
}
func (sp SchedulePlan2) objectFixSql(s global.GetObjectMeta) (res []string, err error) {
	var (
		t     Er.SchemaObjectEr
		r     global.SchemaObjectReturnResult
		event = "[objectFixSql]"
	)
	for drive, db := range map[string]map[string]*sql.DB{
		//GlobalPConfigs.dSns.DestDBName: GlobalPConfigs.DDB,
	} {
		if t, err = getSchemaObjectMetaEr(drive); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v init %v object %v.%v interface fail. error is %v", sp.Object.ObjectLogSeq, event, drive, sp.Object.MSchema, sp.Object.MTable, err))
			return
		}
		if r, err = t.Create(global.SchemaObjectInput{
			DB:               db,
			Type:             sp.Object.Role,
			Schema:           sp.Object.MSchema,
			ObjectDefinition: getCreateSchemaObjectInput(sp.Object.Role, s),
		}); err != nil {
			log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v %v.%v general create sql fail. error is %v", sp.Object.ObjectLogSeq, event, drive, sp.Object.MSchema, sp.Object.MTable, err))
			return
		}
		res = r.FixSql.CreateSql
	}
	return
}
func createObjectPrintSql(plan *SchedulePlan2, c *Meta.Meta) (sourcePrintCreate any, err error) {
	var event = "[createPrintSql]"
	c.Join = "left"
	if sourcePrintCreate, err = c.GetSeqPrintST(); err != nil {
		errorAction(plan, err, fmt.Sprintf("(%v) %v The print create table sql for generating source table %v failed! error Msg: %v", plan.Object.ObjectLogSeq, event, getTableName(plan.Object.Schema, plan.Object.Table), err))
	}
	return
}

func getTableActiveEr(logSeq int64, event, schema, table string) (t Er.TableActiveEr, err error) {
	if t, err = Er.TActive(Er.TableActive{
		DBType: func() string {
			if len(GlobalPConfigs.dSns.DestDBName) > 0 {
				return GlobalPConfigs.dSns.DestDBName
			} else {
				return GlobalPConfigs.dSns.SrcDBName
			}
		}(),
	}); err != nil {
		log.ErrorLog().Error(fmt.Sprintf("(%v) %v.%v init %v object interface fail. error is %v", logSeq, event, schema, table, err))
		return
	}
	return
}
func getObjectConvertPrintInfo(role string, s global.GetObjectMeta) (res *string) {
	switch role {
	case "job":
		res = &s.JobResult.Src.Convert.PrintInfo
	case "view":
		res = &s.ViewResult.Src.Convert.PrintInfo
	case "seq":
		res = &s.SeqResult.Src.Convert.PrintInfo
	case "trigger":
		res = &s.TriggerResult.Src.Convert.PrintInfo
	case "foreign":
		res = &s.ForeignResult.Src.Convert.PrintInfo
	case "func":
		res = &s.FuncResult.Src.Convert.PrintInfo
	case "procedure":
		res = &s.ProcedureResult.Src.Convert.PrintInfo
	case "package":
		res = &s.PackageResult.Src.Convert.PrintInfo
	case "type":
		res = &s.TypeResult.Src.Convert.PrintInfo
	}
	return
}
func (sp SchedulePlan2) getApplySqlSlice(s global.GetObjectMeta) (applySqlSlice []string) {
	//处理应用sql集合
	switch GlobalPConfigs.repair.DataFix {
	case "table":
		for _, v := range s.ObjectFixSql {
			applySqlSlice = append(applySqlSlice, v)
		}
	case "file":
		headInfo := fmt.Sprintf("/* \n------schema:`%s` object:`%s` type:%v ddl ------\n", sp.Object.MSchema, sp.Object.MTable, sp.Object.Role)
		applySqlSlice = append(applySqlSlice, headInfo)
		applySqlSlice = append(applySqlSlice, *getObjectConvertPrintInfo(sp.Object.Role, s))
		endInfo := fmt.Sprintf("*/\n")
		applySqlSlice = append(applySqlSlice, endInfo)
		for _, v := range s.ObjectFixSql {
			applySqlSlice = append(applySqlSlice, v)
		}
	default:
		return
	}
	return
}
func (sp SchedulePlan2) getTableActiveInput(alterStatus bool, applySqlSlice []string) global.TableActiveInput {
	return global.TableActiveInput{
		LogSeq: sp.Object.ObjectLogSeq,
		//DB:     GlobalPConfigs.DDB,
		TableInfo: global.TableInfo{
			Schema:    sp.Object.MSchema,
			Table:     sp.Object.MTable,
			ShardName: "single",
		},
		Input: global.StartPartConfigInputP{
			SqlLogBin: GlobalPConfigs.rules.Object.SqlLogBin,
			SqlMode:   GlobalPConfigs.rules.Object.SqlMode,
		},
		Alter: global.Alter{
			Active:    GlobalPConfigs.repair.DataFix,
			SqlGather: applySqlSlice,
			FilePoint1: func() outPut.FileOperations {
				if getDataFixFileSingleFay() {
					return sp.fixFile.SchemaFilePoint
				}
				if alterStatus {
					return sp.fixFile.AlterFilePoint
				} else {
					return sp.fixFile.CreateFilePoint
				}
			}(),
		},
	}
}

func (sp SchedulePlan2) generalXmlData(behavior string, fixSql, rawSql, execRecord, comment string, err error) {
	sp.XlsResult.Data.Object = getResultSheet(behavior, GlobalPConfigs.repair.DataFix, execRecord, fmt.Sprintf("%v", err), fixSql, rawSql)
	sp.XlsResult.Data.Comment = comment
	newCell.Write(*sp.XlsResult)
}

func (sp SchedulePlan2) execObjectFixSql(s GetObjectMeta) (err error) {
	//var (
	//	event         = "[execObjectFixSql]"
	//	t             Er.TableActiveEr
	//	applySqlSlice []string
	//	behavior      = func() string {
	//		if s.AlterStatus {
	//			return "alter"
	//		}
	//		return "create"
	//	}()
	//)
	//if t, err = getTableActiveEr(sp.Object.ObjectLogSeq, event, sp.Object.Schema, sp.Object.Table); err != nil {
	//	return err
	//}
	//if applySqlSlice = sp.getApplySqlSlice(s); len(applySqlSlice) == 0 {
	//	return
	//}
	//if _, err = t.Alter(sp.getTableActiveInput(s.AlterStatus, applySqlSlice)); err != nil {
	//	log.ErrorLog().Error(fmt.Sprintf("(%v) %v %v general create sql fail. error is %v", sp.Object.ObjectLogSeq, event, "dest", err))
	//	PlanContext.TPodTableStatusSwap(sp.TPod.Status, "error")
	//	sp.generalXmlData(behavior, strings.Join(s.ObjectFixSql, "\n"), *getObjectConvertPrintInfo(sp.Object.Role, s), "执行失败", "", err)
	//} else {
	//	sp.generalXmlData(behavior, strings.Join(s.ObjectFixSql, "\n"), *getObjectConvertPrintInfo(sp.Object.Role, s), "执行成功", "", err)
	//}
	return
}
